
// Main header
#include "../Main.h"

// My header
#include "Widget.h"

// Fridge headers
#include "../Controller/Controller_Main.h"
#include "../Input.h"
#include "../InputKeys.h"
#include "../TypedList.h"

Widget* Widget::s_pxFocus = 0;

Widget::Widget ( Widget* pxParent )
{
	m_uAnchors = ANCHOR_TOP | ANCHOR_LEFT;

	if( pxParent )
	{
		pxParent->AddChild( this );
	}

	m_pxChildren = 0;
	m_pxParent = pxParent;

	m_uPositionX = 0;
	m_uPositionY = 0;

	m_uWidth = 0;
	m_uHeight = 0;

	m_bVisible = true;
	m_bModified = false;

	m_pfnOnBeginDragDrop = 0;
	m_pfnOnDragOver = 0;
	m_pfnOnEndDragDrop = 0;
	
	m_uWindowOriginalWidth = Controller_Main::GetWidth();
	m_uWindowOriginalHeight = Controller_Main::GetHeight();

	m_pUserData = 0;
	m_uUserDataSize = 0;
	
	m_xDefaultCursor = INPUT_MOUSE_CURSOR_ARROW;
}


Widget::~Widget ( )
{
	if( s_pxFocus == this )
	{
		s_pxFocus = 0;
	}
	if( m_pxChildren )
	{
		for( u_int uChild = 0; uChild < m_pxChildren->Count(); ++uChild )
		{
			// First of all, we null our childrens parents pointer, this is so that
			// our children dont try to call RemoveChild on us
			Widget* pxChild = m_pxChildren->Get( uChild );
			if( pxChild )
			{
				pxChild->m_pxParent = 0;
			}
			// Then delete
			delete pxChild;
		}

		delete m_pxChildren;
		m_pxChildren = 0;
	}

	// If we have a parent, unlink ourselves from them
	if( m_pxParent )
	{
		m_pxParent->RemoveChild( this );
	}

	delete[] static_cast<char*>(m_pUserData);
}


void Widget::SetPosition ( const u_int uX, const u_int uY )
{
	const int iOffsetX = int(uX) - m_uPositionX;
	const int iOffsetY = int(uY) - m_uPositionY;
	if( m_pxChildren )
	{
		for( u_int uChild = 0; uChild < m_pxChildren->Count(); ++uChild )
		{
			Widget* pxChild = m_pxChildren->Get( uChild );
			if( pxChild )
			{
				pxChild->SetPosition( pxChild->GetOriginalX() + iOffsetX, pxChild->GetOriginalY() + iOffsetY );
			}
		}
	}
	m_uPositionX = uX;
	m_uPositionY = uY;
}


void Widget::SetAnchors ( const u_int uAnchors )
{
	m_uAnchors = uAnchors;
}


void Widget::SetSize ( const u_int uWidth, const u_int uHeight )
{
	m_uWidth = uWidth;
	m_uHeight = uHeight;
}


void Widget::AddChild ( Widget* pxChild )
{
	if( CanHaveChildren() )
	{
		if( !m_pxChildren )
		{
			m_pxChildren = new TypedList<Widget>( false );
		}

		m_pxChildren->Add( pxChild );
	}
}

void Widget::RemoveChild ( Widget* pxChild )
{
	if( pxChild && m_pxChildren )
	{
		m_pxChildren->Remove( pxChild );
	}
}

void Widget::Update ( )
{
	if( MouseInWidget() )
	{
		const Input* pxInput = Controller_Main::GetInputHandler();
		
		pxInput->SetCursor( m_xDefaultCursor );
		
		if( pxInput && pxInput->MouseButtonPressed( INPUT_MOUSE_BUTTON_LEFT ) && CanGetFocus() )
		{
			if( s_pxFocus )
			{
				s_pxFocus->OnLoseFocus();
			}
			s_pxFocus = this;
			OnGetFocus();
		}
	}
	if( m_pxChildren )
	{
		for( u_int uChild = 0; uChild < m_pxChildren->Count(); ++uChild )
		{
			Widget* pxChild = m_pxChildren->Get( uChild );
			if( pxChild )
			{
				pxChild->Update();
			}
		}
	}
}

void Widget::Render ( )
{
	if( !m_bVisible )
	{
		return;
	}

	if( m_pxChildren )
	{
		for( u_int uChild = 0; uChild < m_pxChildren->Count(); ++uChild )
		{
			Widget* pxChild = m_pxChildren->Get( uChild );
			if( pxChild )
			{
				pxChild->Render();
			}
		}
	}
}


bool Widget::CanHaveChildren ( ) const
{
	return false;
}


bool Widget::MouseInWidget ( ) const
{
	const Input* pxInputHandler = Controller_Main::GetInputHandler();
	
	if( pxInputHandler )
	{
		if	(	( pxInputHandler->MousePositionX() > GetResizedX() )
			&&	( pxInputHandler->MousePositionX() < GetResizedX() + GetResizedWidth() )
			&&	( pxInputHandler->MousePositionY() > GetResizedY() )
			&&	( pxInputHandler->MousePositionY() < GetResizedY() + GetResizedHeight() ) )
		{
			return true;
		}
	}

	return false;
}

bool Widget::MouseInAreaOfWidget( u_int uX, u_int uY, u_int uWidth, u_int uHeight ) const
{
	const Input* pxInputHandler = Controller_Main::GetInputHandler();
	
	if( pxInputHandler )
	{
		if	(	( pxInputHandler->MousePositionX() > GetResizedX() + uX )
			&&	( pxInputHandler->MousePositionX() < GetResizedX() + uX + uWidth )
			&&	( pxInputHandler->MousePositionY() > GetResizedY() + uY )
			&&	( pxInputHandler->MousePositionY() < GetResizedY() + uY + uHeight ) )
		{
			return true;
		}
	}

	return false;
}

Widget* Widget::FindChildWidgetAt( const u_int uX, const u_int uY )
{
	// Find the widget at this position, we take the lowest widget in the tree (i.e. the one rendered topmost)

	if	(	( uX > GetResizedX() )
		&&	( uX < GetResizedX() + GetResizedWidth() )
		&&	( uY > GetResizedY() )
		&&	( uY < GetResizedY() + GetResizedHeight() ) )
	{
		if( m_pxChildren )
		{
			for( u_int uChild = 0; uChild < m_pxChildren->Count(); ++uChild )
			{
				Widget* pxChild = m_pxChildren->Get( uChild );
				if( pxChild )
				{
					Widget* pxFound = pxChild->FindChildWidgetAt( uX, uY );
					if( pxFound )
					{
						return pxFound;
					}
				}
			}
		}
		return this;
	}

	return 0;
}

void Widget::DeleteChildren ( )
{
	if( CanHaveChildren() && m_pxChildren )
	{
		m_pxChildren->Clear();
	}
}

u_int Widget::GetResizedX ( ) const
{
	if( ( m_uAnchors & ANCHOR_RIGHT ) && !( m_uAnchors & ANCHOR_LEFT ) )
	{
		if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
		{
			return m_pxParent->GetResizedX() + m_pxParent->GetResizedWidth() - ( m_pxParent->GetOriginalWidth() - m_uPositionX );
		}

		return Controller_Main::GetWidth() - ( m_uWindowOriginalWidth - m_uPositionX );
	}

	if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
	{
		return m_pxParent->GetResizedX() + ( m_uPositionX - m_pxParent->GetOriginalX() );
	}

	return m_uPositionX;
}

u_int Widget::GetResizedY ( ) const
{
	if( ( m_uAnchors & ANCHOR_BOTTOM ) && !( m_uAnchors & ANCHOR_TOP ) )
	{
		if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
		{
			return m_pxParent->GetResizedY() + m_pxParent->GetResizedHeight() - ( m_pxParent->GetOriginalHeight() - m_uPositionY );
		}

		return Controller_Main::GetHeight() - ( m_uWindowOriginalHeight - m_uPositionY );
	}

	if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
	{
		return m_pxParent->GetResizedY() + ( m_uPositionY - m_pxParent->GetOriginalY() );
	}

	return m_uPositionY;
}

u_int Widget::GetResizedWidth ( ) const
{
	// TODO: Parent relative anchoring
	if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
	{
		return m_uWidth;
	}

	if( ( m_uAnchors & ANCHOR_RIGHT ) && ( m_uAnchors & ANCHOR_LEFT ) )
	{
		return m_uWidth + ( Controller_Main::GetWidth() - m_uWindowOriginalWidth );
	}

	return m_uWidth;
}

u_int Widget::GetResizedHeight ( ) const
{
	// TODO: Parent relative anchoring
	if( m_uAnchors & ANCHOR_PARENT_RELATIVE )
	{
		return m_uHeight;
	}

	if( ( m_uAnchors & ANCHOR_TOP ) && ( m_uAnchors & ANCHOR_BOTTOM ) )
	{
		return m_uHeight + ( Controller_Main::GetHeight() - m_uWindowOriginalHeight );
	}

	return m_uHeight;
}

void Widget::SetBeginDragDropCallback( const BeginDragDropCallback pfnBeginDragDrop )
{
	m_pfnOnBeginDragDrop = pfnBeginDragDrop;

}

void Widget::SetDragOverCallback( const DragOverCallback pfnDragOver )
{
	m_pfnOnDragOver = pfnDragOver;
}

void Widget::SetEndDragDropCallback( const EndDragDropCallback pfnEndDragDrop )
{
	m_pfnOnEndDragDrop = pfnEndDragDrop;
}

void Widget::SetUserData( const void* pUserData, const u_int uDataSize )
{
	if( !pUserData )
	{
		return;
	}
	
	m_pUserData = new char[uDataSize];
	memcpy( m_pUserData, pUserData, uDataSize );
	m_uUserDataSize = uDataSize;
}

void Widget::GetUserData( void* &pUserData, u_int &uDataSize ) const
{
	pUserData = m_pUserData;
	uDataSize = m_uUserDataSize;
}

const void* Widget::GetUserData() const
{
	return m_pUserData;
}

void Widget::ScissorToWidget( bool bScissor )
{
	if( bScissor )
	{
			glEnable( GL_SCISSOR_TEST );
			glScissor( static_cast<int>( GetResizedX() ), 
					   static_cast<int>( Controller_Main::GetHeight() - ( GetResizedY() + GetResizedHeight() ) ), 
					   static_cast<int>( GetResizedWidth() ), 
					   static_cast<int>( GetResizedHeight() ) );
	}
	else
	{
		glDisable( GL_SCISSOR_TEST );
	}
}

//eof
